home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Graphics / PostScript / EnhancedYap / Source / YapDocument.m < prev    next >
Encoding:
Text File  |  1995-06-12  |  10.9 KB  |  392 lines

  1. /*
  2.  *  YapDocument.m
  3.  *  Author: Ali Ozer
  4.  *  Created: Aug 28, 1988
  5.  *  Modified for 0.8: Sep 1988
  6.  *  Modified for 0.9 and revised: Feb & Mar 1989
  7.  *  Modified for 1.0 and nibified: Jun & Jul 1989
  8.  *  Modified for 2.0 and zonified: Aug 1990
  9.  *  Modified: Jan 92 for 3.0. Localized.
  10.  *
  11.  *  YapDocument class implements the Yap documents --- for every open 
  12.  *  window, we have another instance of the YapDocument class. Each instance
  13.  *  loads itself into a separate zone.
  14.  *
  15.  *  You may freely copy, distribute and reuse the code in this example.
  16.  *  NeXT disclaims any warranty of any kind, expressed or implied,
  17.  *  as to its fitness for any particular use.
  18.  */
  19.  
  20. #import <appkit/appkit.h>
  21. #import <objc/NXBundle.h>
  22. #import <objc/error.h>
  23. #import <libc.h>
  24. #import <string.h>
  25. #import <sys/file.h>
  26.  
  27. #import "YapDocument.h"
  28. #import "PSText.h"
  29. #import "YapApp.h"
  30. #import "YapOutput.h"
  31. #import "FindPanel.h"
  32.  
  33. #import <objc/NXBundle.h>
  34. #import <objc/List.h>
  35. #import <objc/zone.h>
  36. #import <streams/streams.h>
  37. #import <defaults/defaults.h>
  38. #import <mach/mach.h>
  39. #import <libc.h>
  40. #import <string.h>
  41.  
  42. #define UNTITLED NXLocalString ("UNTITLED", NULL, "Name of default document")
  43. #define CANTWRITEFILE_STRING NXLocalString ("Can't write file.", NULL, "Document could not be saved")
  44. #define CLOSEWINDOW_STRING NXLocalString("Close", NULL, "Request to close window containing unsaved document from menu or close button.")
  45. #define SAVECHANGES_STRING NXLocalString("%s has changes. Save them?", NULL, "Question asked of user when he/she tries to close a window containing an unsaved document.  The %s is the name of the document.")
  46. #define SAVE_STRING NXLocalString("Save", NULL, "Button choice which allows the user to save the document.")
  47. #define DONTSAVE_STRING NXLocalString("Don't Save", NULL, "Button choice which allows the user to abort the save of a document which is being closed.")
  48. #define OK_STRING NXLocalString ("OK", NULL, "Default response in alert panel")
  49. #define CANCEL_STRING NXLocalString ("Cancel", NULL, "Button choice allowing user to cancel the request to close a window")
  50.  
  51. #define XOFFSET 5.0     // Offset of subsequent windows
  52. #define YOFFSET -20.0
  53. #define MAXSIZE 1.0e38    // Maximum size of a text object
  54.  
  55. @implementation YapDocument
  56.  
  57. /*
  58.  * The next two methods allow us to cache/reuse zones.
  59.  */
  60. static id zoneList = nil;
  61.  
  62. + (NXZone *)newZone
  63. {
  64.     if (!zoneList || ![zoneList count]) {
  65.     return NXCreateZone(vm_page_size, vm_page_size, YES);
  66.     } else {
  67.     return (NXZone *)[zoneList removeLastObject];
  68.     }
  69. }
  70.  
  71. + (void)reuseZone:(NXZone *)aZone
  72. {
  73.     if (!zoneList) zoneList = [List new];
  74.     [zoneList addObject:(id)aZone];
  75. }
  76.  
  77. /*
  78.  * Return the document in the specified window.
  79.  */
  80. + documentForWindow:window
  81. {
  82.     id del = [window delegate];
  83.     return (del && [del isKindOf:[YapDocument class]]) ? del : nil;
  84. }
  85.  
  86.  
  87. /*
  88.  * Create a new instance of YapDocument with the specified stream in the
  89.  * buffer.  If the file cannot be opened, no document is created and nil
  90.  * is returned.
  91.  */
  92. + newFromStream:(NXStream *)stream name:(const char *)fileName
  93. {
  94.     id docWin;        /* Window belonging to this document. */
  95.     id textObj;        /* The text object we put in the window. */
  96.     NXRect textFrame;    /* The frame of the text object in our window */
  97.  
  98.  
  99.     self = [[self allocFromZone:[self newZone]] init];
  100.     
  101.     if (![NXApp loadNibSection:"Document.nib" owner:self withNames:NO fromZone:[self zone]]) {
  102.         NXLogError ("Can't find Document.nib!");    
  103.         NXCloseMemory (stream, NX_FREEBUFFER);
  104.         [self free];
  105.         return nil;
  106.         }
  107.  
  108.     /*
  109.      * Loading the nib file above sets the document outlet to the
  110.      * scrollview; so we can use this outlet to get at the window & such.
  111.      */
  112.     docWin = [document window];
  113.     [[document docView] getFrame:&textFrame];
  114.  
  115.     /*
  116.      * Put the window offset from the previous document window... If no
  117.      * previous window exists, or the main window is undetermined, then
  118.      */ 
  119.     if ([NXApp mainWindow]) {
  120.         NXRect winFrame, winLoc;
  121.         [[NXApp mainWindow] getFrame:&winFrame];
  122.         [[docWin class] getContentRect:&winLoc forFrameRect:&winFrame style:[docWin style]];
  123.         [docWin moveTo:NX_X(&winLoc) + XOFFSET :NX_Y(&winLoc) + YOFFSET];
  124.         }
  125.     
  126.     [self setName:UNTITLED];
  127.     [docWin setDelegate:self];
  128.  
  129.     if (stream) {
  130.         char *text;
  131.         int len, maxLen;
  132.         NXGetMemoryBuffer (stream, &text, &len, &maxLen);
  133.         textObj = [[PSText allocFromZone:[self zone]] initFrame:&textFrame text:text alignment:NX_LEFTALIGNED];
  134.         if (fileName)
  135.             [self setName:fileName];
  136.             else
  137.             {
  138.             [self setName:UNTITLED];
  139.             [[document window] setDocEdited:YES];
  140.             };
  141.         NXCloseMemory (stream, NX_FREEBUFFER);
  142.         } else {
  143.         textObj = [[PSText allocFromZone:[self zone]] initFrame:&textFrame];
  144.         [self setName:UNTITLED];
  145.         }
  146.  
  147.     /*
  148.      * Put this new text object in the window and free the IB-created one.
  149.      */
  150.     [[document setDocView:textObj] free];
  151.  
  152.     /*
  153.      * Set various parameters.
  154.      */
  155.     [document setAutoresizeSubviews:YES];
  156.     [textObj setVertResizable:YES];        // Grow down as you type
  157.     [textObj setHorizResizable:NO];        // But not sideways 
  158.     [textObj setAutosizing:NX_WIDTHSIZABLE];    // Size horizontally when resized
  159.     [textObj setMonoFont:YES];
  160.     [textObj setOpaque:YES];
  161.     [textObj setMinSize:&textFrame.size];
  162.     NX_WIDTH(&textFrame) = NX_HEIGHT(&textFrame) = MAXSIZE;
  163.     [textObj setMaxSize:&textFrame.size];    // Can grow
  164.     [textObj setSel:0:0];            // Set the selection
  165.     [textObj setDelegate:self];
  166.     [textObj sizeToFit];
  167.  
  168.     [docWin makeKeyAndOrderFront:self];
  169.  
  170.     [self initializePrintInfo];
  171.  
  172.     return self;
  173. }
  174.  
  175.  
  176.  
  177. /*
  178.  * Create a new instance of YapDocument with the specified file in the
  179.  * buffer.  If the file cannot be opened, no document is created and nil
  180.  * is returned.
  181.  */
  182. + newFromFile:(const char *)fileName
  183. {
  184.     NXStream *stream = NULL;
  185.     id docWin;        /* Window belonging to this document. */
  186.     id textObj;        /* The text object we put in the window. */
  187.     NXRect textFrame;    /* The frame of the text object in our window */
  188.  
  189.     if (fileName && !(stream = NXMapFile(fileName, NX_READONLY))) 
  190.         {
  191.         return nil;
  192.         }
  193.     return [self newFromStream:stream name:fileName];
  194. }
  195.  
  196. + new
  197. {
  198.     return [self newFromFile:NULL];
  199. }
  200.  
  201. - initializePrintInfo
  202. {
  203.     static BOOL printInfoInitialized = NO;
  204.     if (!printInfoInitialized) {
  205.     [[NXApp printInfo] setVertCentered:NO];
  206.     [[NXApp printInfo] setHorizCentered:NO];
  207.     [[NXApp printInfo] setHorizPagination:NX_FITPAGINATION];
  208.     [[NXApp printInfo] setMarginLeft:36.0 right:36.0 top:72.0 bottom:72.0];
  209.     printInfoInitialized = YES;
  210.     }
  211.     return self;
  212. }
  213.  
  214. /*
  215.  * Checks to see if the window has been edited...
  216.  */
  217. - (BOOL)needsSaving
  218. {
  219.     return [[document window] isDocEdited];
  220. }
  221.  
  222. /*
  223.  * Delegate method for the document Text object. We use this method
  224.  * to detect when the text in the window is modified.
  225.  */
  226. - text:text isEmpty:(BOOL)empty
  227. {
  228.     if (![[document window] isDocEdited]) {
  229.         [[document window] setDocEdited:YES];
  230.     }
  231.     return NO;
  232. }
  233.  
  234. /*
  235.  * Delegate method for the document Text object. We use this method
  236.  * to detect when the font is changed so we an write it out as the default.
  237.  */
  238. - textWillConvert:textObject fromFont:oldFont toFont:newFont
  239. {
  240.     if (newFont) {
  241.         char str[80];
  242.     sprintf (str, "%f", [newFont pointSize]);
  243.     NXWriteDefault ([NXApp appName], "NXFontSize", str);
  244.     NXWriteDefault ([NXApp appName], "NXFont", [newFont name]);
  245.     [Text setDefaultFont:newFont];
  246.     }
  247.  
  248.     return newFont;
  249. }
  250.  
  251. /*
  252.  * saveDocument: will write out the contents of the document
  253.  * to the specified file. 
  254.  * 
  255.  * If fileName is NULL, asks user for a file name.
  256.  * Returns NO if the user decides to cancel the operation.
  257.  * Otherwise returns YES (whether or not the document could be saved),
  258.  * modifying the needsSaving state.
  259.  */
  260. - (BOOL)saveDocument:(const char *)fileName
  261. {
  262.     int fd;     // File descriptor
  263.     NXStream *stream = NULL;
  264.     BOOL saveOK;
  265.  
  266.     if (!fileName || !strcmp (fileName, UNTITLED) || !strcmp (fileName, "")) {
  267.     if (!(fileName = [[SavePanel new] runModalForDirectory:"." file:UNTITLED] ? [[SavePanel new] filename] : NULL)) {
  268.         return NO;
  269.     }
  270.     }
  271.  
  272.     if (saveOK = 
  273.         (((fd = open (fileName, O_WRONLY|O_CREAT|O_TRUNC, 0644)) != -1) &&
  274.         (stream = NXOpenFile (fd, NX_WRITEONLY))))
  275.     [[document docView] writeText:stream];
  276.     if (stream) NXClose (stream);
  277.     if (fd != -1) close (fd);
  278.  
  279.     if (saveOK) {
  280.     [self setName:fileName];
  281.     [[document window] setDocEdited:NO];
  282.     } else {
  283.     NXRunAlertPanel (NULL, CANTWRITEFILE_STRING, OK_STRING, NULL, NULL);
  284.     }
  285.     return YES;
  286. }
  287.  
  288. /*
  289.  * Set/Get the name of the document. This is the same as the title.
  290.  * Free the old name after the new one is set, because sometimes this
  291.  * routine might be called as the old name as the argument...
  292.  */
  293. - setName:(const char *)documentName
  294. {
  295.     documentName = documentName ? documentName : "";
  296.     if (documentName != name) {
  297.     free(name);
  298.     name = NXCopyStringBufferFromZone (documentName, [self zone]); 
  299.     [[document window] setTitleAsFilename:name];
  300.     }
  301.     return self;
  302. }
  303.  
  304. -(const char *)name
  305. {
  306.     return name;
  307. }
  308.  
  309. /*
  310.  * windowWillClose: gets called by windows who have this instance of 
  311.  * YapDocument as delegate.  We call closeDocument:andWindow: to see if the
  312.  * document needs saving and take the appropriate action if so. If the user
  313.  * cancels the save, closeDocument:andWindow: returns NO and we return nil.
  314.  * This prevents the window from closing...
  315.  */
  316. - windowWillClose:sender
  317. {
  318.     return [self closeDocument:CLOSEWINDOW_STRING andWindow:NO] ? self : nil;
  319. }
  320.  
  321. /*
  322.  * Closes the document. If document needs saving, asks user he/she'd like the doc
  323.  * saved. Returns NO if the user cancels out of the save operation, otherwise returns YES.
  324.  * flag determines if the window should also be closed with the document.
  325.  */
  326. - (BOOL)closeDocument:(const char *)message andWindow:(BOOL)flag
  327. {
  328.     if ([self needsSaving]) {
  329.     int save = NXRunAlertPanel(message, SAVECHANGES_STRING, SAVE_STRING, DONTSAVE_STRING, CANCEL_STRING, [self name]);
  330.     if (save == NX_ALERTOTHER) {    // Cancel
  331.         return NO;
  332.     } else if (save == NX_ALERTDEFAULT) {
  333.         [self save:nil];
  334.     }
  335.     }
  336.     [[document window] setDelegate:nil];
  337.     if (flag) [[document window] close];
  338.     [self free];
  339.     return YES;
  340. }
  341.  
  342. - free
  343. {
  344.     NXZone *docZone = [self zone];
  345.     if (name) free(name);
  346.     [super free];
  347.     [YapDocument reuseZone:docZone];
  348.     return nil;
  349. }
  350.     
  351. /*
  352.  * save: saves the current document. If the document is untitled, it 
  353.  * puts up a savePanel to get the user to enter a file name. saveAs:
  354.  * saves the document under a new name by putting up a savePanel.
  355.  */
  356. - save:sender 
  357. {
  358.     (void)[self saveDocument:[self name]];
  359.     return self;
  360. }
  361.   
  362. - saveAs:sender 
  363. {
  364.     if ([[SavePanel new] runModalForDirectory:"." file:[self name]]) {
  365.         (void)[self saveDocument:[[SavePanel new] filename]];
  366.     }
  367.     return self;
  368. }
  369.  
  370. - execute:sender
  371. {
  372.     [[NXApp outputView] executeCodeFrom:[document docView]];
  373.  
  374.     return self;
  375. }   
  376.  
  377. /*
  378.  * To get around the problem of printPSCode: going up the responder chain and
  379.  * causing print panel to come back after Cancel, we use the following glue.
  380.  */
  381. - print:sender
  382. {
  383.     [[document docView] printPSCode:sender];
  384.     return self;
  385. }
  386.  
  387.  
  388.  
  389.  
  390.  
  391. @end
  392.